// metody poczenia/dania
- (NSString*)_sendRequest:(NSURL *)theURL
          withRequestType:(SuperCheckoutRequestType)requestType
             responseType:(SuperCheckoutResponseType)responseType
{
   ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:theURL];
   [request setDelegate:self];
   NSString *requestIdentifier = [NSString stringWithNewUUID];
   [request setUserInfo:[NSDictionary dictionaryWithObjectsAndKeys:
                          [NSNumber numberWithInt:requestType], REQUEST_TYPE,
                          [NSNumber numberWithInt:responseType], RESPONSE_TYPE,
                          requestIdentifier, REQUEST_ID,
                          nil]];

   if (request == nil)
   {
      return nil;
   }

   if ([self _isValidDelegateForSelector:@selector(connectionStarted:)])
      [delegate connectionStarted:[[request requestID] stringValue]];

   [request startAsynchronous];
   return requestIdentifier;
}

- (NSString *)_sendRequestWithMethod:(NSString *)method
                                path:(NSString *)path
                     queryParameters:(NSDictionary *)params
                                body:(NSString *)body
                         requestType:(SuperCheckoutRequestType)requestType
                        responseType:(SuperCheckoutResponseType)responseType
{
   NSURL *theUrl = [self _baseURLWithMethod:method
                                       path:path
                                requestType:requestType
                            queryParameters:params];

   return [self _sendRequest:theUrl
             withRequestType:requestType
                responseType:responseType];
}

- (NSString *)_sendImageRequestWithURL:(NSString *)imageURL
{
   NSURL *theURL = [NSURL URLWithString:imageURL];

   return [self _sendRequest:theURL
             withRequestType:SuperCheckoutProductImage
                responseType:SuperCheckoutImage];
}

- (NSURL *)_baseURLWithMethod:(NSString *)method
                         path:(NSString *)path
                  requestType:(SuperCheckoutRequestType)requestType
              queryParameters:(NSDictionary *)params
{
   // przygotowanie odpowiedniego cigu tekstowego adresu URL
   NSString *fullPath = [path
      stringByAddingPercentEscapesUsingEncoding:NSNonLossyASCIIStringEncoding];
   if (params && ![method isEqualToString:HTTP_POST_METHOD])
   {
      fullPath = [self _queryStringWithBase:fullPath parameters:params prefixed:YES];
   }

   NSString *connectionType = @"http";

   NSString *urlString = nil;
   if(requestType == SuperCheckoutProductImage)
   {
      urlString = path;
   } else {
      urlString = [NSString stringWithFormat:@"%@://%@/%@",
                     connectionType,
                     BASE_URL, fullPath];
   }

   NSURL *finalURL = [NSURL URLWithString:urlString];
   return finalURL;
}

// metody przetwarzania
- (void)_parseDataForConnection:(ASIHTTPRequest *)request
{

   NSData *jsonData = [[[request responseData] copy] autorelease];
   NSString *identifier = [[[[request requestID] stringValue] copy] autorelease];

   SuperCheckoutRequestType requestType =
      [[[request userInfo] objectForKey:REQUEST_TYPE] intValue];
   SuperCheckoutResponseType responseType =
      [[[request userInfo] objectForKey:RESPONSE_TYPE] intValue];

   switch ([[[request userInfo] objectForKey:RESPONSE_TYPE] intValue])
   {
      case SuperCheckoutProductList:
      case SuperCheckoutCartContents:
         [SCJSONParser parserWithJSON:jsonData
                             delegate:self
                 connectionIdentifier:identifier
                          requestType:requestType
                         responseType:responseType
                                  URL:nil];
         break;

      default: 
         break; 
   }
}

// metody delegata
- (BOOL) _isValidDelegateForSelector:(SEL)selector
{
   return ((delegate != nil) && [delegate respondsToSelector:selector]);
}

#pragma mark - Metody ASIHTTPRequestDelegate

- (void)requestFinished:(ASIHTTPRequest *)request
{
   NSString *requestIdentifier = [[request userInfo] objectForKey:REQUEST_ID];
   if ([request responseStatusCode] >= 400)
   {
      // W przypadku awarii informujemy delegata.
      NSData *receivedData = [request responseData];
      NSString *body = [receivedData length] ? [NSString
         stringWithUTF8String:[receivedData bytes]] : @"";

      NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
                                  [request responseString], @"response",
                                  body, @"body",
                                  nil];
      NSError *error = [NSError errorWithDomain:@"HTTP"
                                           code:[request responseStatusCode]
                                       userInfo:userInfo];
      if ([self 
         _isValidDelegateForSelector:@selector(requestFailed:withError:)])
            [delegate requestFailed:requestIdentifier withError:error];

      // zerwanie poczenia

      NSString *connectionIdentifier = requestIdentifier;
         [connections removeObjectForKey:connectionIdentifier];
      if ([self _isValidDelegateForSelector:@selector(connectionFinished:)])
         [delegate connectionFinished:connectionIdentifier];
      return;
   }

   NSString *connID = nil;
   SuperCheckoutResponseType responseType = 0;
   connID = requestIdentifier;
   responseType = [[[request userInfo] objectForKey:RESPONSE_TYPE] intValue];

   // poinformowanie delegata
   [delegate requestSucceeded:connID];

   NSData *receivedData = [request responseData];
   if (receivedData)
   {
      if (responseType == SuperCheckoutImage)
      {
         // utworzenie obrazu na podstawie danych
         UIImage *image = [[[UIImage alloc] initWithData:receivedData] autorelease];

         // poinformowanie delegata
         if ([self 
            _isValidDelegateForSelector:@selector(imageReceived:forRequest:)])
               [delegate imageReceived:image forRequest:requestIdentifier];
      } else {
         // przetworzenie danych pochodzcych z poczenia (XML lub JSON)
         [self _parseDataForConnection:request];
      }
   }

   // usunicie zasobw poczenia
   [connections removeObjectForKey:connID];
   if ([self _isValidDelegateForSelector:@selector(connectionFinished:)])
      [delegate connectionFinished:connID];
}

- (void)requestFailed:(ASIHTTPRequest *)request
{
   NSString *requestIdentifier = [[request userInfo] objectForKey:REQUEST_ID];;

   // poinformowanie delegata
   if ([self _isValidDelegateForSelector:@selector(requestFailed:withError:)])
   {
      [delegate requestFailed:requestIdentifier
                    withError:[request error]];
   }

   // usunicie zasobw poczenia
   [connections removeObjectForKey:requestIdentifier];
   if ([self _isValidDelegateForSelector:@selector(connectionFinished:)])
      [delegate connectionFinished:requestIdentifier];
}

#pragma mark - Metody SCJSONParserDelegate

- (void)parsingSucceededForRequest:(NSString *)identifier
                    ofResponseType:(SuperCheckoutResponseType)responseType
                     parsedObjects:(NSDictionary *)parsedObject
{
   switch (responseType)
   {
      case SuperCheckoutProductList:
         if([self
            _isValidDelegateForSelector:@selector(productListReceived:forRequest:)])
         {
               NSArray *result = [parsedObject objectForKey:@"result"];
               NSMutableArray *newResult =
                  [NSMutableArray arrayWithCapacity:[result count]];

               for(NSDictionary *obj in result)
               {
                  Product *prod = [[Product alloc] initWithDictionary:obj];
                  [newResult addObject:prod];
                  [prod release];
               }

               [delegate productListReceived:[NSArray arrayWithArray:newResult]
                                   forRequest:identifier];
         }
         break;
      case SuperCheckoutCartContents:
         if([self
            _isValidDelegateForSelector:@selector(cartContentsReceived:forRequest:)])
         {
               id cart = [parsedObject objectForKey:@"result"];
               if([cart isKindOfClass:[NSNull class]])
               {
                  cart = nil;
               }
               [delegate cartContentsReceived:cart forRequest:identifier];
         }

      default:
         break;
   }
}
